﻿using System;

using Outlook = Microsoft.Office.Interop.Outlook;
using System.Data.SqlServerCe;
using System.Diagnostics;


namespace Investafinance.Sync.Shell
{
    /// <summary>
    ///   Обалочка для форм-регион outlook-а здесь перехватываются все необходимые события
    ///     такие как сохранение формы и открытие...
    /// </summary>
    public class ShellFormRegion<TItem> where TItem : class
    {
        // строка конекта к бызе данных
        private string scon;//= @"Data Source=C:\Meta.sdf";   
        // флаг определяет изменяем мы данные в случае сохранения или добавляем новые (будим считать что добавляем)
        private bool IsUpdate = false;                      
        // не сохранять форму
        private bool flagSaveFormRegion = false;            
        // удаляем?
        private bool flagDeleteItem = false;
        //будит хранить TItem
        private TItem CurrentItem;
        //Название таблиц
        private string nameTable;

        public ShellFieldItem<TItem> CollectionField;


        public void PropertyChange(string nameFieldOutlook)
        {
            if (CollectionField != null)
            {
                string nameFieldSharePoin = ShellFieldItem<TItem>.GetAssociateNameSP(nameFieldOutlook,associateMap);
                if (nameFieldSharePoin != null)
                {
                    CollectionField.collectionField[nameFieldSharePoin] = CollectionField[nameFieldOutlook];
                    //CollectionField[nameFieldSharePoin, nameFieldOutlook] = "";
                }
            }
        }

        private string[,] associateMap;

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="OutlookItem">Указател  на Item в Outlook собственно для которого создаётся обалочка</param>
        /// <param name="scon">Строка конекта к БД</param>
        /// <param name="nameTable">Название синхронизируемой таблици (должно совподать с названием листа SharePoint)</param>
        /// <param name="CollectionField">Набор полей типа ShellFieldItem</param>
        public ShellFormRegion(TItem OutlookItem, string scon, string nameTable, ref ShellFieldItem<TItem> CollectionField)
        {
            Debug.Assert(OutlookItem != null, "ShellFormRegion.cs конструктор OutlookItem != null");
            Debug.Assert(scon != null, "ShellFormRegion.cs конструктор scon != null");
            Debug.Assert(scon != "", "ShellFormRegion.cs конструктор scon != ''");
            Debug.Assert(nameTable != null, "ShellFormRegion.cs конструктор nameTable != null");
            Debug.Assert(nameTable != "", "ShellFormRegion.cs конструктор nameTable != ''");

            associateMap = CollectionField.associateMap;

            CurrentItem = OutlookItem;
            this.scon = scon;
            this.nameTable = nameTable;
            this.CollectionField = CollectionField;

            //бред но подругому никак
            if (CurrentItem is Outlook.TaskItem)
            {
                //зарегистрируем все обработчики событий
                ((Outlook.TaskItem)CurrentItem).Write += new Outlook.ItemEvents_10_WriteEventHandler(DeligateWrite);
                //событие при удалении
                ((Outlook.TaskItem)CurrentItem).BeforeDelete += new Outlook.ItemEvents_10_BeforeDeleteEventHandler(DeligateDelete);
                ((Outlook.TaskItem)CurrentItem).PropertyChange += new Microsoft.Office.Interop.Outlook.ItemEvents_10_PropertyChangeEventHandler(PropertyChange);
            }
            else
                if (CurrentItem is Outlook.ContactItem)
                {
                    //зарегистрируем все обработчики событий
                    ((Outlook.ContactItem)CurrentItem).Write += new Outlook.ItemEvents_10_WriteEventHandler(DeligateWrite);
                    //событие при удалении
                    ((Outlook.ContactItem)CurrentItem).BeforeDelete += new Outlook.ItemEvents_10_BeforeDeleteEventHandler(DeligateDelete);
                    ((Outlook.ContactItem)CurrentItem).PropertyChange += new Microsoft.Office.Interop.Outlook.ItemEvents_10_PropertyChangeEventHandler(PropertyChange);
                }
                else
                    if (CurrentItem is Outlook.PostItem)
                    {
                        //зарегистрируем все обработчики событий
                        ((Outlook.PostItem)CurrentItem).Write += new Outlook.ItemEvents_10_WriteEventHandler(DeligateWrite);
                        //событие при удалении
                        ((Outlook.PostItem)CurrentItem).BeforeDelete += new Outlook.ItemEvents_10_BeforeDeleteEventHandler(DeligateDelete);
                        ((Outlook.PostItem)CurrentItem).PropertyChange += new Microsoft.Office.Interop.Outlook.ItemEvents_10_PropertyChangeEventHandler(PropertyChange);
                    }

            SqlCeConnection conn = new SqlCeConnection(scon);
            try
            {
                //открываем подключение 
                conn.Open();

                string Mileage = "";
                if (CurrentItem is Outlook.TaskItem)
                {
                    Mileage = ((Outlook.TaskItem)CurrentItem).Mileage;
                }
                else
                    if (CurrentItem is Outlook.ContactItem)
                    {
                        Mileage = ((Outlook.ContactItem)CurrentItem).Mileage;
                    }
                    else
                        if (CurrentItem is Outlook.PostItem)
                        {
                            Mileage = ((Outlook.PostItem)CurrentItem).Mileage;
                        }

                SqlCeCommand cmd;
                SqlCeDataReader dr;
                if ((Mileage != null) && (Mileage != ""))
                {
                    //в этом случае мы выполняем загрузку полей из базы так как Item откроем на редактирование
                    cmd = new SqlCeCommand("select * from Sync_" + nameTable + " where ID='" + Mileage + "'", conn);
                    dr = cmd.ExecuteReader(System.Data.CommandBehavior.SingleRow);
                    //если да то читаем значения и записываем в соотвецтвующие поля
                    if (dr.Read())
                    {
                        for (int i = 0; i < dr.VisibleFieldCount; i++)
                        {
                            string s1 = dr.GetName(i);
                            if (s1 != "Modify")
                            if (s1 != "EntryIDInOutlook")    
                            {
                                string s2 = dr[i].ToString();
                                this.CollectionField.AddField(s1, s2);
                            }
                        }
                        IsUpdate = true;
                    }
                }
                else
                {
                    //в этом случае мы создали вовый Item ->  все поля пустые
                    //посмотрим наименование полей в таблице
                    cmd = new SqlCeCommand("select * from Sync_" + nameTable + " where ID=''", conn);
                    dr = cmd.ExecuteReader();
                    for (int i = 0; i < dr.VisibleFieldCount; i++)
                    {
                        string s1 = dr.GetName(i);
                        if (s1 != "Modify")
                        if (s1 != "EntryIDInOutlook")
                        {
                            this.CollectionField.AddField(s1, "");
                        }
                    }
                    //добавим поля которые нам нужны UserProperties (если такие есть)
                    string[,] data =  associateMap;
                    if (data != null)
                    {
                        for (int i = 0; i < Convert.ToInt32(data.Length / 4); i++)
                        {
                            if (data[i, 1].IndexOf("UserProperties") != -1)
                            {
                                Outlook.OlUserPropertyType up;

                                switch (data[i, 3])
                                {
                                    case "olOutlookInternal":
                                        up = Outlook.OlUserPropertyType.olCombination;
                                        break;
                                    case "olCurrency":
                                        up = Outlook.OlUserPropertyType.olCurrency;
                                        break;
                                    case "olDateTime":
                                        up = Outlook.OlUserPropertyType.olDateTime;
                                        break;
                                    case "olDuration":
                                        up = Outlook.OlUserPropertyType.olDuration;
                                        break;
                                    case "olEnumeration":
                                        up = Outlook.OlUserPropertyType.olEnumeration;
                                        break;
                                    case "olFormula":
                                        up = Outlook.OlUserPropertyType.olFormula;
                                        break;
                                    case "olInteger":
                                        up = Outlook.OlUserPropertyType.olInteger;
                                        break;
                                    case "olNumber":
                                        up = Outlook.OlUserPropertyType.olNumber;
                                        break;
                                    case "olPercent":
                                        up = Outlook.OlUserPropertyType.olPercent;
                                        break;
                                    case "olSmartFrom":
                                        up = Outlook.OlUserPropertyType.olSmartFrom;
                                        break;
                                    case "olText":
                                        up = Outlook.OlUserPropertyType.olText;
                                        break;
                                    case "olYesNo":
                                        up = Outlook.OlUserPropertyType.olYesNo;
                                        break;
                                    default:
                                        up = Outlook.OlUserPropertyType.olText;
                                        break;
                                }

                                //получим имя или число
                                string s = data[i, 1].Replace("UserProperties", "").Replace("[", "").Replace("]", "");
                                             

                                if (CurrentItem is Outlook.TaskItem)
                                {
                                    try
                                    {
                                        //попробуем преобразовать в число
                                        Convert.ToInt32(s);
                                        //если число тогда назавём своёство также как и колонку
                                        ((Outlook.TaskItem)CurrentItem).UserProperties.Add(data[i, 2], up, null, 3);
                                    }
                                    catch
                                    {
                                        //если exeption то это имя UserProperties
                                        ((Outlook.TaskItem)CurrentItem).UserProperties.Add(s, up, null, 3);
                                    }
                                    
                                }
                                else
                                    if (CurrentItem is Outlook.ContactItem)
                                    {
                                        try
                                        {
                                            //попробуем преобразовать в число
                                            Convert.ToInt32(s);
                                            //если число тогда назавём свойство также как и колонку
                                            ((Outlook.ContactItem)CurrentItem).UserProperties.Add(data[i, 2], up, null, 3);
                                        }
                                        catch
                                        {
                                            //если exeption то это имя UserProperties
                                            ((Outlook.ContactItem)CurrentItem).UserProperties.Add(s, up, null, 3);
                                        }
                                    }
                                    else
                                        if (CurrentItem is Outlook.PostItem)
                                        {
                                            try
                                            {
                                                //попробуем преобразовать в число
                                                Convert.ToInt32(s);
                                                //если число тогда назавём свойство также как и колонку
                                                ((Outlook.PostItem)CurrentItem).UserProperties.Add(data[i, 2], up, null, 3);
                                            }
                                            catch
                                            {
                                                //если exeption то это имя UserProperties
                                                ((Outlook.PostItem)CurrentItem).UserProperties.Add(s, up, null, 3);
                                            }
                                            
                                        }
                                        else
                                            if (CurrentItem is Outlook.MailItem)
                                            {
                                                try
                                                {
                                                    //попробуем преобразовать в число
                                                    Convert.ToInt32(s);
                                                    //если число тогда назавём свойство также как и колонку
                                                    ((Outlook.MailItem)CurrentItem).UserProperties.Add(data[i, 2], up, null, 3);
                                                }
                                                catch
                                                {
                                                    //если exeption то это имя UserProperties
                                                    ((Outlook.MailItem)CurrentItem).UserProperties.Add(s, up, null, 3);
                                                }
                                            }
                            }
                        }
                    }
                }
                dr.Close();
            }
            catch (Exception err)
            {
                Debug.Assert(false, "ShellFormRegion.cs ShellFormRegion() конструктор" + "\n" + err.Message + "\n -->" + err.Source + "\n"); 
            }
            finally
            {
                //закрываем подключение
                conn.Close();
                conn.Dispose();
                conn = null;
            }
            CollectionField = this.CollectionField;
        }

        public void Close()
        {
            flagSaveFormRegion = true;
        }

        private void DeligateWrite(ref bool Cancel)
        {

            if (CurrentItem != null)
            {
                if (!flagDeleteItem)
                {
                    if (!flagSaveFormRegion)
                        Save_CurrentForm();
                }
            }
            if (CurrentItem is Outlook.TaskItem)
            {
                ((Outlook.TaskItem)CurrentItem).Write -= new Outlook.ItemEvents_10_WriteEventHandler(DeligateWrite);
            }
            else
                if (CurrentItem is Outlook.ContactItem)
                {
                    ((Outlook.ContactItem)CurrentItem).Write -= new Outlook.ItemEvents_10_WriteEventHandler(DeligateWrite);
                }
                else
                    if (CurrentItem is Outlook.PostItem)
                    {
                        ((Outlook.PostItem)CurrentItem).Write -= new Outlook.ItemEvents_10_WriteEventHandler(DeligateWrite);
                    }
        }

        private void DeligateDelete(object Item, ref bool Cancel)
        {
            flagDeleteItem = true;
        }

        public void Save_CurrentForm()
        {
 

            try
            {
                if (CurrentItem is Outlook.ContactItem)
                {
                    //если мы синхронизируем  Body то надо внести изменения в свою колекцию т.к почему-то propertychange не видит Body
                    if (ShellFieldItem<TItem>.GetAssociateName("Comments" , associateMap) != null)
                        CollectionField.collectionField["Comments"] = CollectionField["Body"];
                    //если мы синхронизируем  Body то надо внести изменения в свою колекцию т.к почему-то propertychange не видит FirstName
                    if (ShellFieldItem<TItem>.GetAssociateName("FirstName" , associateMap) != null)
                        CollectionField.collectionField["FirstName"] = CollectionField["FirstName"];
                    //если мы синхронизируем  Body то надо внести изменения в свою колекцию т.к почему-то propertychange не видит Title
                    if (ShellFieldItem<TItem>.GetAssociateName("Title" , associateMap) != null)
                        CollectionField.collectionField["Title"] = CollectionField["LastName"];
                }

                SqlCeConnection conn = new SqlCeConnection(scon);
                try
                {
                    //открываем подключение к бызе
                    conn.Open();
                    SqlCeCommand cmd;
                    if (!IsUpdate)
                    {
                        //Создаём новую запись
                        string MyEntryID = DateTime.Now.ToString()+"::"+DateTime.Now.Millisecond.ToString() + " $,$ " + ShellFolderSync<TItem>.localApplication.GetNamespace("MAPI").CurrentUser.Name; 
                        
                        //сохраним дбы назначить EntryID
                        string CurentEntryID = ""; 
                        if (CurrentItem is Outlook.TaskItem)
                        {
                            ((Outlook.TaskItem)CurrentItem).Save();
                            CurentEntryID = ((Outlook.TaskItem)CurrentItem).EntryID;
                            ((Outlook.TaskItem)CurrentItem).Mileage = MyEntryID;
                        }
                        else
                            if (CurrentItem is Outlook.ContactItem)
                            {
                                ((Outlook.ContactItem)CurrentItem).Save();
                                CurentEntryID = ((Outlook.ContactItem)CurrentItem).EntryID;
                                ((Outlook.ContactItem)CurrentItem).Mileage = MyEntryID;
                            }
                            else
                                if (CurrentItem is Outlook.PostItem)
                                {
                                    ((Outlook.PostItem)CurrentItem).Save();
                                    CurentEntryID = ((Outlook.PostItem)CurrentItem).EntryID;
                                    ((Outlook.PostItem)CurrentItem).Mileage = MyEntryID;
                                }
                        string sName = "(";
                        string sValue = "(";
                        for (int i = 0; i < CollectionField.GetCountField(); i++)
                        {
                            if (CollectionField.GetNameField(i) != "owshiddenversion")
                            if (CollectionField.GetNameField(i) != "ID")
                            {
                                sName += CollectionField.GetNameField(i) + ",";
                                sValue += "'" + CollectionField.GetValueField(i) + "',";
                            }
                        }
                        sName += "Modify,EntryIDInOutlook,ID,owshiddenversion)";
                        sValue += "'I','" + CurentEntryID + "','" + MyEntryID + "','1')";

                        string s = "INSERT INTO Sync_" + nameTable + " " + sName + " VALUES " + sValue;
                        cmd = new SqlCeCommand(s, conn);
                    }
                    else
                    {
                        //Изменяем
                        //для начала узнаем EntryID
                        string CurentEntryID = "";
                        if (CurrentItem is Outlook.TaskItem)
                        {
                            CurentEntryID = ((Outlook.TaskItem)CurrentItem).Mileage;
                        }
                        else
                            if (CurrentItem is Outlook.ContactItem)
                            {
                                CurentEntryID = ((Outlook.ContactItem)CurrentItem).Mileage;
                            }
                            else
                                if (CurrentItem is Outlook.PostItem)
                                {
                                    CurentEntryID = ((Outlook.PostItem)CurrentItem).Mileage;
                                }
                        /*Cинтаксис 
                          UPDATE [DatabaseName1!]TableName1
                          SET Column_Name1 = eExpression1
                          [, Column_Name2 = eExpression2 ...]
                          WHERE FilterCondition1 [AND | OR FilterCondition2 ...]
                        */
                        //определим U или I мы делаем
                        //может оказаться так что в одной эпохе синхранизации мы сделали I  затем решили изменить...
                        string mod = "";
                        string owshiddenversion = "";
                        //инициализируем Command 
                        cmd = new SqlCeCommand("select Modify,owshiddenversion from Sync_" + nameTable + " where ID='" + CurentEntryID + "'", conn);
                        //выполняем селект
                        SqlCeDataReader dr = cmd.ExecuteReader();

                        //если да то читаем значения и записываем в соотвецтвующие поля
                        if (dr.Read())
                        {
                            if (dr["Modify"].ToString() == "I")
                            {
                                mod = "I";
                                owshiddenversion = dr["owshiddenversion"].ToString();
                            }
                            else
                            {
                                mod = "U";
                                if (dr["Modify"].ToString() == "U")
                                    owshiddenversion = dr["owshiddenversion"].ToString();
                                else
                                    owshiddenversion = (Convert.ToInt32(dr["owshiddenversion"].ToString()) + 1).ToString();
                            }
                        }
                        dr.Close();

                        string sSet = "UPDATE Sync_" + nameTable + " SET ";
                        for (int i = 0; i < CollectionField.GetCountField(); i++)
                        {
                            if (CollectionField.GetNameField(i) != "owshiddenversion")
                            if (CollectionField.GetNameField(i) != "ID")
                            {
                                sSet += CollectionField.GetNameField(i) + " = '" + CollectionField.GetValueField(i) + "',";
                            }
                        }
                        sSet += "Modify='" + mod + 
                                "',owshiddenversion='" + owshiddenversion + 
                                "'  WHERE ID   ='" + CurentEntryID + "';";
                        

                        cmd = new SqlCeCommand(sSet, conn);
                    }
                    cmd.ExecuteNonQuery();
                    cmd.Dispose();
                }
                catch (Exception err)
                {
                    Debug.Assert(false, "ShellFormRegion.cs Save_CurrentForm()" + "\n" + err.Message + "\n -->" + err.Source + "\n"); 
                }
                finally
                {
                    //закрываем подключение к базе 
                    conn.Close();
                    conn.Dispose();
                    conn = null;
                }
            }
            catch (Exception ex)
            {
                Debug.Assert(false, "ShellFormRegion.cs Save_CurrentForm()" + "\n" + ex.Message + "\n -->" + ex.Source + "\n"); 
            }
        }
    }
}
